package com.charon.common.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.HashMap;

/**
  *@ClassName: ControllerLogAspect
  *@Description: Controller层Aop日志
  *@Author: Charon [248135724@qq.com]
  *@Date: 2018/12/24 0024 11:07
  *@Version: 1.0
  */

/**
 * 注意：加了@Aspect注解就不要再加@Component注解了，加了之后会相当于加载两个相同的Bean，
 * 将会导致项目失败，报如下错误。
 *  Error creating bean with name 'projectingArgumentResolverBeanPostProcessor' defined in class path resource
 *  [org/springframework/data/web/config/ProjectingArgumentResolverRegistrar.class]:
 */
@Aspect
public class ControllerLogAspect {

    private static final Logger LOGGER = LoggerFactory.getLogger(ControllerLogAspect.class);

    // 开始时间
    private long beginTime = 0L;
    // 结束时间
    private long endTime = 0L;

    //需要拦截的类：第一个*为方法的返回值
    @Pointcut(value="execution(* *..controller..*.*(..))")
    public void beforePointCut(){ }

    @Pointcut(value="execution(* *..controller..*.*(..))")
    public void afterReturnPointCut(){}


    @Before(value = "execution(* *..controller..*.*(..))&&@target(org.springframework.stereotype.Controller)",argNames = "joinPoint")
    public void doBeforeInServiceLayer(JoinPoint joinPoint) {
        String classType=joinPoint.getTarget().getClass().getName();
        String methodName=joinPoint.getSignature().getName();
        Object[] args=joinPoint.getArgs();
        beginTime = System.currentTimeMillis();
        Class<?>[] classes=new Class[args.length];
        for (int k = 0; k < args.length; k++) {
            if (!args[k].getClass().isPrimitive()) {
                // 获取的是封装类型而不是基础类型
                String result = args[k].getClass().getName();
                Class s = map.get(result);
                classes[k] = s == null ? args[k].getClass() : s;
            }
        }
        ParameterNameDiscoverer pnd = new DefaultParameterNameDiscoverer();
        // 获取指定的方法，第二个参数可以不传，但是为了防止有重载的现象，还是需要传入参数的类型
        Method method = null;
        try {
            method = Class.forName(classType).getMethod(methodName, classes);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        // 参数名
        String[] parameterNames = pnd.getParameterNames(method);
        // 通过map封装参数和参数值
        HashMap<String, Object> paramMap = new HashMap();
        StringBuilder sb=new StringBuilder();
        for (int i = 0; i < parameterNames.length; i++) {
            paramMap.put(parameterNames[i], args[i]);
            sb.append("  "+parameterNames[i]+"："+args[i]);
        }
        LOGGER.info("开始计时: {} 类：{}  方法：{}  参数：{}", new SimpleDateFormat("HH:mm:ss.SSS").format(beginTime),classType,methodName,sb.toString());
    }

    //@AfterReturning(value = "afterReturnPointCut()&&@annotation(org.springframework.stereotype.Controller)",argNames = "",returning = "returnParam")
    @AfterReturning(value = "execution(* *..controller..*.*(..))&&@target(org.springframework.stereotype.Controller)",argNames = "joinPoint")
    public void afterReturn(JoinPoint joinPoint){
        String classType=joinPoint.getTarget().getClass().getName();
        String methodName=joinPoint.getSignature().getName();
        LOGGER.info("结束计时: {} 类：{}  方法：{}", new SimpleDateFormat("HH:mm:ss.SSS").format(beginTime),classType,methodName);
    }

    private static HashMap<String,Class> map=new HashMap<String, Class>(){
        {
            put("java.lang.Integer",int.class);
            put("java.lang.Double", double.class);
            put("java.lang.Float", float.class);
            put("java.lang.Long", long.class);
            put("java.lang.Short", short.class);
            put("java.lang.Boolean", boolean.class);
            put("java.lang.Char", char.class);
        }
    };

    //@Around包含了@Before@After@AfterThrow的范围
    @Around(value="execution(* *..controller..*.*(..))")
    public Object handlerControllerMethod(ProceedingJoinPoint pjp) throws Throwable{
        System.out.println("切片：Aspect Start");
        //获取请求参数
        Object[] args=pjp.getArgs();
        for(Object arg:args){
            System.out.println("请求参数："+arg);
        }
        long start = System.currentTimeMillis();
        //执行Controller的方法后的返回值
        Object object=pjp.proceed();
        System.out.println("执行结果："+object);
        long end = System.currentTimeMillis();
        System.out.println("切片：Aspect End 耗时："+(start-end));
        return object;
    }

    @Around(value="execution(* *..web..*.*(..))")
    public Object handlerWebMethod(ProceedingJoinPoint pjp) throws Throwable{
        System.out.println("切片：Aspect Start");
        //获取请求参数
        Object[] args=pjp.getArgs();
        for(Object arg:args){
            System.out.println("请求参数："+arg);
        }
        long start = System.currentTimeMillis();
        //执行Controller的方法后的返回值
        Object object=pjp.proceed();
        System.out.println("执行结果："+object);
        long end = System.currentTimeMillis();
        System.out.println("切片：Aspect End 耗时："+(start-end));
        return object;
    }

}
